package com.plugins.pluginlib.internal;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Build;
import android.text.TextUtils;

import com.plugins.pluginlib.IBasePluginActivity;
import com.plugins.pluginlib.IProxyActivity;
import com.plugins.pluginlib.utils.IConstants;
import com.plugins.pluginlib.utils.SoLibManager;

import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;

import dalvik.system.DexClassLoader;

/**
 * 插件管理
 */
public class IPluginManager {

    private static final String TAG = "IPluginManager";

    //插件里的so文件存放位置
    private static final String pluginPath = "pluginlib";
    /**
     *  加载成功
     */
    public static final int START_RESULT_SUCCESS = 0;

    /**
     * 没找到插件包
     */
    public static final int START_RESULT_NO_PKG = 1;

    /**
     * 没找到class
     */
    public static final int START_RESULT_NO_CLASS = 2;

    /**
     * 报错
     */
    public static final int START_RESULT_TYPE_ERROR = 3;

    /**
     * 没找到启动
     */
    public static final int START_RESULT_NO_LAUNCHER = 4;

    private static IPluginManager sInstance;
    private Context mContext;
    private final HashMap<String, IPluginPackage> mPackagesHolder = new HashMap<String, IPluginPackage>();

    private int mFrom = IConstants.FROM_INTERNAL;

    private String mNativeLibDir = null;

    private int mResult;

    private IPluginManager(Context context) {
        mContext = context.getApplicationContext();
        mNativeLibDir = mContext.getDir(pluginPath, Context.MODE_PRIVATE).getAbsolutePath();
    }

    public static IPluginManager getInstance(Context context) {
        if (sInstance == null) {
            synchronized (IPluginManager.class) {
                if (sInstance == null) {
                    sInstance = new IPluginManager(context);
                }
            }
        }

        return sInstance;
    }

    /**
     *加载插件执行执行
     *  当插件已加载不重复加载直接返回已加载的
     * @param dexPath
     */
    public IPluginPackage loadApk(String dexPath) {
        return loadApk(dexPath, true);
    }

    /**
     * @param dexPath
     *            插件路径
     * @param hasSoLib
     *           是否有so文件
     * @return
     */
    public IPluginPackage loadApk(final String dexPath, boolean hasSoLib) {
        mFrom = IConstants.FROM_EXTERNAL;

        PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath,
                PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
        if (packageInfo == null) {
            return null;
        }

        IPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
        if (hasSoLib) {
            copySoLib(dexPath);
        }

        return pluginPackage;
    }

    /**
     * 预备插件的环境
     * @param packageInfo
     * @param dexPath
     * @return
     */
    private IPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath) {

        IPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
        if (pluginPackage != null) {
            return pluginPackage;
        }
        DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
        AssetManager assetManager = createAssetManager(dexPath);
        Resources resources = createResources(assetManager);
        pluginPackage = new IPluginPackage(dexClassLoader, resources, packageInfo);
        mPackagesHolder.put(packageInfo.packageName, pluginPackage);
        return pluginPackage;
    }

    private String dexOutputPath;

    /**
     * 加载dex
     * @param dexPath
     * @return
     */
    private DexClassLoader createDexClassLoader(String dexPath) {
        File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE);
        dexOutputPath = dexOutputDir.getAbsolutePath();
        DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir, mContext.getClassLoader());
        return loader;
    }

    private AssetManager createAssetManager(String dexPath) {
        try {
            AssetManager assetManager = AssetManager.class.newInstance();
            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
            addAssetPath.invoke(assetManager, dexPath);
            return assetManager;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    public IPluginPackage getPackage(String packageName) {
        return mPackagesHolder.get(packageName);
    }

    private Resources createResources(AssetManager assetManager) {
        Resources superRes = mContext.getResources();
        Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
        return resources;
    }

    /**
     * 复制 *.so 文件到 pluginlib 文件夹.
     * 
     * @param dexPath
     */
    private void copySoLib(String dexPath) {
        SoLibManager.getSoLoader().copyPluginSoLib(mContext, dexPath, mNativeLibDir);
    }

    /**
     * 启动activity
     */
    public int startActivity(Context context, IIntent iIntent) {
        return startActivityForResult(context, iIntent, -1);
    }

    /**
     *  有返启动activity
     * @param context
     * @param iIntent
     * @param requestCode
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public int startActivityForResult(Context context, IIntent iIntent, int requestCode) {
        if (mFrom == IConstants.FROM_INTERNAL) {
            iIntent.setClassName(context, iIntent.getPluginClass());
            performStartActivityForResult(context, iIntent, requestCode);
            return IPluginManager.START_RESULT_SUCCESS;
        }

        String packageName = iIntent.getPluginPackage();
        if (TextUtils.isEmpty(packageName)) {
            throw new NullPointerException("disallow null packageName.");
        }

        IPluginPackage pluginPackage = mPackagesHolder.get(packageName);
        if (pluginPackage == null) {
            return START_RESULT_NO_PKG;
        }

        final String className = getPluginActivityFullPath(iIntent, pluginPackage);
        Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className);
        if (clazz == null) {
            return START_RESULT_NO_CLASS;
        }

        Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
        if (activityClass == null) {
            return START_RESULT_TYPE_ERROR;
        }

        iIntent.putExtra(IConstants.EXTRA_CLASS, className);
        iIntent.putExtra(IConstants.EXTRA_PACKAGE, packageName);
        iIntent.setClass(mContext, activityClass);
        performStartActivityForResult(context, iIntent, requestCode);
        return START_RESULT_SUCCESS;
    }

    // 重命名 loadPluginActivityClass -> loadPluginClass
    private Class<?> loadPluginClass(ClassLoader classLoader, String className) {
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return clazz;
    }

    private String getPluginActivityFullPath(IIntent iIntent, IPluginPackage pluginPackage) {
        String className = iIntent.getPluginClass();
        className = (className == null ? pluginPackage.defaultActivity : className);
        if (className.startsWith(".")) {
            className = iIntent.getPluginPackage() + className;
        }
        return className;
    }

    /**
     *获取代理activity里的class
     * 
     * @param clazz
     *
     * @return
     */
    private Class<? extends Activity> getProxyActivityClass(Class<?> clazz) {
        Class<? extends Activity> activityClass = null;
        if (IBasePluginActivity.class.isAssignableFrom(clazz)) {
            activityClass = IProxyActivity.class;
        }

        return activityClass;
    }



    private void performStartActivityForResult(Context context, IIntent iIntent, int requestCode) {
        if (context instanceof Activity) {
            ((Activity) context).startActivityForResult(iIntent, requestCode);
        } else {
            context.startActivity(iIntent);
        }
    }
}
